package Cluedo.GUI;

/**
 * Extra required Java API
 */
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.Vector;
import java.util.StringTokenizer;
import java.io.FileNotFoundException;

/**
 * Graphics packages
 */
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

import Cluedo.AI.ComplexAI;

/**
 * For the load from XML files
 */
import org.xml.sax.SAXException;
import javax.xml.parsers.ParserConfigurationException;

/**
 * Dependancies in the Cluedo package
 */
import Cluedo.API.*;
import Cluedo.Controller.*;
import Cluedo.AI.*;
import Cluedo.API.CluedoConfig;
import Cluedo.GUI.Board;
import Cluedo.Game.Card;
import Cluedo.Game.CardCollection;
import Cluedo.Game.Player;
import Cluedo.Game.Position;
import Cluedo.Game.Room;
import Cluedo.Game.Square;
import Cluedo.Game.StartingSquare;

/**
 * This View is a simple GUI for Increment 1.
 *
 * @author alex
 */
public class Simple extends JFrame {

	final static int GUIMajorVersion = 0;
	final static int GUIMinorVersion = 3;
	
	/*
	 * Graphics components to which we need references..
	 * 
	 */
	
	/**
	 * Displays responses from the game
	 */
	JTextArea feedbackConsole;
	/**
	 * User uses to input data and commands
	 */
	JTextField commandLine;
	/**
	 * Panel for the board image
	 */
	JPanel imagePanel;
	/**
	 * Panel holds the feedback from the game
	 */
	JPanel feedbackPanel;
	/**
	 * Panel for the input of the game, jtextfield and jlabel
	 */
	JPanel commandInput;
	/**
	 * Used on the feedbackConsole to give scrolling
	 */
	JScrollPane feedbackConsoleScroller;
	/*
	 * Buffered board image 
	 */
	ImageIcon staticBoardImage = null;
	JLabel boardImage = null;
	
	/** 1 = minimal messages, 2 = all messages, 0 = none */
	int debugLevel;

	
	/**
	 * The board containing the Cluedo Model. 
	 */
	Cluedo.GUI.Board board = null;

	/**
	 * Stored to find when a game is over.
	 */
	boolean gameFinished = false;
	
	/**
	 * The image to display as the board.
	 */
	public static String mapFileName = "default.gif";


	/*
	 * A couple of messages required several times during game-play 
	 */
	final public static String frameTitle = "Cluedo (Group F) GUI "  + GUIMajorVersion + "." + GUIMinorVersion;
	final public static String welcomeMessage = 
			"For a list of commands type 'help', or 'startgame' to being playing\n"+
			"If you need help on how to play the game please type 'instructions'\n\n" +
			"Useful hint: Cards are case sensitive and must be entered without spaces: MrsWhite, DiningRoom.";
	final static String lineSeparator = "-----------------------------------------";
	final static String defaultCharacterInputMessage = "Enter \"NickName CharacterName/Number\": ";
						
	/** Needed for feedback, unfortunately quite messy... */
	Player playerWithCard = null;
	/** Needed for feedback, unfortunately quite messy... */
	Vector cardsFound = null;
								
	/**
	 * AIs to be instantiated from this class 
	 */
	Vector AIs = new Vector();
	boolean AI = true;

 
	/* Start code that handles the interaction of the GUI
	 * 
	 */
	 
	 
	/**
	 * CluedoListener acts as an inbox to receive messages from the GameController and will decide
	 * whether it needs to update any information.
	 *
	 */
	CluedoListener cluedoListener = new CluedoListener() {
	  /**
	   * Messages here are received from the TextHandler and result in some action
	   * being taken locally in this View, commands called from here are located in
	   * an inner-class called LocalEvents.<br>
	   * The return value from the local event is outputted to the user.
	   *
	   */
	  public void localMessage(Cluedo.API.Message message) {
	  	String output = "";
          
	  	if(debugLevel > 0) {
	  		if(!message.type.equals(""))
	  			outputMessage("localMessage: "+message.type +" [" + message + "]\n");
	  		else
	  			outputMessage("localMessage: [" + message + "]\n");
	  	}
	  	try {
	  		/* 
	  		 * Info messages to the View received from the TextParser 
	  		 * 
	  		 * 
	  		 */
	  		if(message.type.equals("quit")) {
	  			output = localEvents.localQuit();
	  		}
	  		if(message.type.equals("errorInParameters") && gameConfig.inDisproveMode() == false) {
	  			output = localEvents.localErrorInParameters(message.data);
	  		}
	  		else if(message.type.equals("disproveFeedback")) {
	  			output = localEvents.localDisproveFeedback(message.data);
	  		}
	  		else if(message.type.equals("formingNewGame")) {
	  			output = localEvents.localFormingNewGame();
	  		}
	  		else if(message.type.equals("gameFormed"))
	  			output = localEvents.localGameFormed();
	  		else if(message.type.equals("addNextPlayer"))
	  			output = localEvents.localAddNextPlayer();
	  		else if(message.type.equals("badGameSize"))
	  			output = localEvents.localBadGameSize();
	  		else if(message.type.equals("badNewPlayerFormat")){
	  			output = localEvents.localBadNewPlayerFormat();
	  		}
	  		else if(message.type.equals("badNewPlayer")){
	  			output = localEvents.localBadNewPlayer();
	  		}
	  		else if(message.type.equals("addedNewPlayer")){
	  			output = localEvents.localAddedNewPlayer();
	  		}
	  		else if(message.type.equals("textHandlerError")) {
	  			output ="textHandlerError!!";
	  		}
	  		else if(message.type.equals("showHelpFile")) {
	  			output = localEvents.localShowConsoleHelp(message.data);
	  		}
	  		else if(message.type.equals("showInstructionsFile")) {
	  			output = localEvents.showInstructionsFile();
	  		}
	  		else if(message.type.equals("startNewGame")) {
	  			output = localEvents.localStartNewGame();
	  		}
	  		else if(message.type.equals("loadScript")) {
	  			output = localEvents.localRunScript((String)message.parameters.elementAt(0));
	  		}
	  		else if(message.type.equals("saveGame")) {
	  			output = localEvents.localSaveGame((String)message.parameters.elementAt(0));
	  		}
	  		else if(message.type.equals("loadGame")) {
	  			output = localEvents.localLoadGame((String)message.parameters.elementAt(0));
	  		}
	  		/* 
	  		 * 
	  		 * Game actions. these require a special output or handling by the View
	  		 * 
	  		 */
	  		else if(message.type.equals("gameAction")) {
	  			if(message.data.equals("movePlayer")) {
	  				output = localEvents.localMovePlayer((String)message.parameters.elementAt(0));
	  			}
	  			if(message.data.equals("end go")) {
	  				output = localEvents.localEndGo();
	  			}
	  			if(message.data.equals("roll")) {
	  				output = localEvents.localRollDice();
	  			}
	  			else if(message.data.equals("info")) {
	  				output = localEvents.localInfo();
	  			}
	  			else if(message.data.equals("uneliminate card")) {
	  				output = localEvents.localUnEliminateCard((String)message.parameters.elementAt(0));
	  			}
	  			else if(message.data.equals("eliminate card")) {
	  				output = localEvents.localEliminateCard((String)message.parameters.elementAt(0));
	  			}
	  			else if(message.data.equals("show cards")) {
	  				output = localEvents.localShowCards();
	  			}
	  			else if(message.data.equals("show detective pad")) {
	  				output = localEvents.localShowDetectivePad();
	  			}
	  			else if(message.data.equals("positions")) {
	  				output = localEvents.localPositions();
	  			}
	  			else if(message.data.equals("suggest")) {
	  				output = localEvents.localSuggest((String)message.parameters.elementAt(0), (String)message.parameters.elementAt(1));
	  			}
	  			else if(message.data.equals("accuse")) {
	  				output = localEvents.localAccuse((String)message.parameters.elementAt(0), (String)message.parameters.elementAt(1), (String)message.parameters.elementAt(2));
	  			}
	  		}
	  	}
	  	catch(Exception localCommandError) {
	  		
	  		if(debugLevel > 0)
	  			outputMessage("Error in localCommand: "+message.type +" [" + message.data + "]\n");
	  		else
	  			outputMessage("There was an error in the command you typed.\n");
	  		return;
	  	}
	  	if(!output.equals(""))
	  		outputMessage(output);
	  }
          
          
	  
	  /** 
	   * Receives messages from the GameController and handles them in GameEvents.
	   * Happens when something is updated in the Model.
	   *
	   */
	  public void notifyAlert(Message message) {
	  	String output = "";
	  	if(debugLevel > 1)
	  		outputMessage("Controller: [" + message.type + "] '" + message.data + "'\n");
          
	  	if(message.type.equals("newRoll")) {
	  		output = gameEvents.notifyNewRoll();
	  	}
	  	else if(message.type.equals("winGame")) {
	  		output = gameEvents.notifyWinGame();
	  	}
	  	else if(message.type.equals("newEnvelope")) {
	  		output = gameEvents.notifyEnvelope(message.data);
	  	}
	  	else if(message.type.equals("nextPlayer")) {
	  		output = gameEvents.notifyTurnChange();
	  	}		
	  	else if(message.type.equals("commenceGame")) {
	  		output = gameEvents.commenceGame();
	  	}
	  	else if(message.type.equals("moveAlert")) {
	  		if(message.parameters.size() == 1)
	  			output = gameEvents.notifyMoveAlert(((Player)message.parameters.firstElement()));
	  		else
	  			output = gameEvents.notifyMoveAlert(null);
	  	}		
	  	
	  	if(!output.equals(""))
	  		outputMessage(output);
	  }
          
	public CluedoListener getCluedoListener() {
		return this;
	}
	  /* End code that handles the interaction of the GUI
	   * 
	   */
  };
		
	
	
	/** The game controller for this View */
	GameController gameController = null;
	/** The parser for text input from this View */
	Cluedo.GUI.TextParser textParser;
	/**  The handler for input from the parser on this View */
	Cluedo.GUI.TextEventHandler textHandler = new Cluedo.GUI.TextEventHandler(this);
	/** Create a game config class to read in and sort out what the new game will be like */
	CluedoConfig gameConfig = new CluedoConfig();

	
	private LocalEvents localEvents = new LocalEvents();
	private GameEvents gameEvents = new GameEvents();

	public CluedoListener getCluedoListener() {
		return cluedoListener;
	}
	
	public CluedoConfig getGameConfig() {
		return gameConfig;
	}
	

	/* End code that handles the interaction of the GUI
	 * 
	 */

	/* Start code that sets up the GUI
	 * 
     */

	public Simple(String frameTitle, String imageFileName) {
		super(frameTitle);

		textParser = new Cluedo.GUI.TextParser();
		
		staticBoardImage = new javax.swing.ImageIcon((Toolkit.getDefaultToolkit().getImage( this.getClass().getResource(imageFileName) )));

		boardImage = new JLabel(staticBoardImage);
		
		board = new Board(imageFileName);
		
		Container contentPane = this.getContentPane();

		imagePanel = new JPanel();
		
		imagePanel.setLayout(new BorderLayout());
		//imagePanel.add(boardImage, BorderLayout.CENTER);
		imagePanel.add(board, BorderLayout.CENTER);
		
		contentPane.setLayout( new BorderLayout() );
		
		feedbackConsole = new JTextArea();
		feedbackConsole.setEditable(false);
		boolean badFont = false;
		
		if(System.getProperty("os.name").startsWith("Linux")) {
			try {
				feedbackConsole.setFont(new Font("Courier", Font.PLAIN,  12));
			}
			catch(Exception nofont) {
				System.out.println("[Simple:] No such font: Courier");
				badFont = true;
			}
			try {
				if(badFont == true) {
					feedbackConsole.setFont(new Font("Console", Font.PLAIN,  12));
				}
			}
			catch(Exception nofont) {
				System.out.println("[Simple:] No such font: Console");
			}
		}
		
		feedbackConsole.setText("");
		
		System.out.println("[Simple:] Resolution detected ["+this.getToolkit().getScreenSize().getWidth()+"]");
		
		// We add extra lines to the feedback console depending on the screen size
		if(this.getToolkit().getScreenSize().getWidth() == 1024)
			feedbackConsole.setRows(9);
		else if(this.getToolkit().getScreenSize().getWidth() == 1280)
			feedbackConsole.setRows(22);
		else if(this.getToolkit().getScreenSize().getWidth() == 1600)
			feedbackConsole.setRows(30);			
		else if(this.getToolkit().getScreenSize().getWidth() > 1024 && 
				this.getToolkit().getScreenSize().getWidth() < 1208)
			feedbackConsole.setRows(14);
		else {
			feedbackConsole.setRows(9);
			System.out.println("[Simple:] Warning unsupported resolution detected !!");
		}
		
		commandLine = new JTextField();
		commandLine.addActionListener(new TextInputListener());
		addWindowListener( new WindowAdapter() {
		   public void windowOpened( WindowEvent e ){
		        commandLine.requestFocus();
		     }
		   } ); 


		feedbackConsoleScroller = new JScrollPane(feedbackConsole);
		
		JLabel inputHere = new JLabel("Input: ");
		inputHere.setDisplayedMnemonic(KeyEvent.VK_I);
		inputHere.setLabelFor(commandLine);
		
		feedbackPanel = new JPanel();
		feedbackPanel.setLayout(new BorderLayout());
		
		commandInput = new JPanel();
		commandInput.setLayout(new BorderLayout());
		commandInput.add(inputHere, BorderLayout.WEST);
		commandInput.add(commandLine, BorderLayout.CENTER);

	
		feedbackPanel.add(feedbackConsoleScroller, BorderLayout.CENTER);
		
		feedbackPanel.add(commandInput, BorderLayout.SOUTH);

		contentPane.add(imagePanel, BorderLayout.NORTH);		
		contentPane.add(feedbackPanel, BorderLayout.SOUTH);
		
		setResizable(false);

	}
	/**
	 * Used to load game from file.
	 * 
	 * @param fileName
	 */
	public void loadGame(String fileName) {
		localEvents.localLoadGame(fileName);
	}

	/**
	 * Used in new game creation.
	 * 
	 * @param gameConfig
	 */
	public void newGame(CluedoConfig gameConfig) {
		this.gameConfig = gameConfig;
		localEvents.createNewGame(false);
	}

	public void processWindowEvent(WindowEvent e) {
		super.processWindowEvent(e);
		if (e.getID() == WindowEvent.WINDOW_CLOSING) {
			System.out.println("Thanks for playing.");
			System.exit(0);
		}
	}
	
	/**
	 * Output a string to the console, if no carridge return is added then
	 * two will be added for you.
	 * @param message	The message to be displayed in the console.
	 *
	 */
	public void outputMessage(String message) {
		// This makes sure an extra space is added, because in the game
		// forming phase no extra space is added after a prompt.

		if(message.charAt(message.length()-1) == '\n')
			feedbackConsole.append(message+ "\n");
		else
			feedbackConsole.append(message+ "\n\n");

		// Reset the position of the caret to the end of the text box so that an "auto-scroll" effect appears
		feedbackConsole.setCaretPosition(feedbackConsole.getText().length()-1);
	}
	
	/**
	 * Configure the gameController for this View
	 * @param gameController An initialised gameController
	 */
	public void setGameController(GameController gameController) {
		this.gameController = gameController;
	}
	
	public GameController getGameController() {
		return gameController;
	}
	
	/**
	 * Creates the Simple class, packs and displays it.
	 * @param args One argument == <debug_level>, two arguments == <debug_level> <map_file>
	 *
	 */
	public static void main( String args[] ) {

		// Set the UI appearance
		try {
			Resources.configureUI();
		}
		catch(Exception error) {}
		
		Simple simple = null;

		if(args.length == 2) {
			if(args[1].startsWith("."))
				simple = new Simple(frameTitle, args[1]);
			else
				simple = new Simple(frameTitle, "Maps/"+ args[1]);
		}
		else
			simple = new Simple(frameTitle, "Maps/" + mapFileName);
			
		// New instance of the main game frame
		

		// Get and set the debug level
		if(args.length ==1) {
			try {
				simple.debugLevel = Integer.parseInt(args[0]);
			}catch(Exception Error) {
				simple.debugLevel = 2;
			}
		}
		else
			simple.debugLevel = 0;

		// Configure frame
		simple.setSize(200, 200);
		simple.pack();
		simple.setVisible(true);

		// Extra messages 
		simple.outputMessage( "# Welcome to "+ frameTitle + " !\n" +
				      "# Running on Java version: "+ System.getProperty("java.version") );
		if(simple.debugLevel > 0)
			simple.outputMessage("Creating new game controller.\n");
		//Create a new controller 
		simple.gameController = new Cluedo.Controller.GameController();
		
		if(simple.debugLevel > 0)
			simple.outputMessage("Bind to game controller.\n");
		
		// Register as a new Listener with the controller 
		simple.gameController.bindListener(simple.getCluedoListener());
		
		simple.outputMessage( welcomeMessage );

	
	}
	

	
	/* Application code and handling here */

	/**
	 * Internal listener handles the command-line
	 */
	class TextInputListener implements ActionListener {
		/**
		 * @param e Some automatically-fed ActionEvent.
		 */
		public void actionPerformed(ActionEvent e) {
			
			String line = commandLine.getText();
			String [] parts;
			
			if(!commandLine.getText().equals("")) {
				try {
					parts = textParser.parseCommandLine(line);
					if( gameConfig.isFormingGame() == false && gameConfig.inDisproveMode() == false) {
						if(parts != null)
							textHandler.handleCommandLine(parts);
						else
							outputMessage("Invalid command, see \"help\"\n");
					}
					else {
						textHandler.handleCommandLine( (new String[]{line}) );
					}

				//	commandLine.setText("");
				}
				catch(Exception error){
					outputMessage("An error occoured with your previous command.\n");
				}
			}
			commandLine.setText("");
		}
	}



	/**
	 * This inner-class handles all messages from the controller, these messages are GAME-LEVEL
	 * based, and indicate the View needs to update itself.
	 * 
	 */ 
	class GameEvents {
		
		private String notifyWinGame() {
			String output = "";
			
			output = "Accusation correct, " +
		    		gameController.board.getCurrentPlayer().getRealName() +
		    		" wins the game!\n";
			gameFinished = true;

		    return output;
		}
		
		private	String notifyMoveAlert(Player who) {
			String output = "";
			Position newPosition = gameController.board.getCurrentPlayer().getPosition();
			String type = "square";
			String roomPrefix = "";
			String squarePrefix = "";
		
			if(who == null) {
				roomPrefix = "You have";
				squarePrefix = "You are";
			}
			else {
				roomPrefix = who.getRealName() + " has";
				squarePrefix = who.getRealName() + " is";
			}
				
			if(newPosition instanceof Room)
				type = "room";
			if(type.equals("room"))
				output = roomPrefix + " entered the "+newPosition.getID()+".\n";
			else
				output = squarePrefix + " now at position "+newPosition.getID()+".\n";

			return output;
		}
		
		private	String notifyNewRoll() {
			String output = "";
			
			output = "You rolled " + gameController.board.diceThrow1 + " and "+ gameController.board.diceThrow2 + ".\n" +
				 "You may move " + gameController.board.getCurrentPlayer().getMovesRemaining() + " squares.\n";
			return output;
		}

		private String commenceGame () {
			String output = "";
			Player currentPlayer = gameController.board.getCurrentPlayer();

			if(currentPlayer.getPlayerType() >= 1)
				commandLine.setEditable(false);
			else if(commandLine.isEditable() == false)
					commandLine.setEditable(true);

				output = "\nEveryone has rolled the dice and the player with the highest score was "+currentPlayer.getRealName() +".\n" +
					"- "+ currentPlayer.getRealName()  + ", it's now your turn to play.\n\n" +
					localEvents.findOptions()+"";

			return output;
		}

		private	String notifyTurnChange() {
			String output = "";
			Player currentPlayer = gameController.board.getCurrentPlayer();

			if(currentPlayer.getPlayerType() >= 1)
				commandLine.setEditable(false);
			else if(commandLine.isEditable() == false)
					commandLine.setEditable(true);

			if(currentPlayer.isInteractive() == false) {
				System.out.println("[Simple:notifyTurnChange] Skipping " + currentPlayer.getRealName()+ "'s turn");
				//output += "Skipping the turn of " + currentPlayer.getRealName();
				gameController.directCommand("nextPlayer", null);
				return output;
			}

			output = "\n" + gameController.board.getCurrentPlayer().getRealName() + ", it is now your turn.\n\n"+localEvents.findOptions()+"";

			return output;
		}		
		
		private	String notifyEnvelope(String cardString) {
			String output = "";
			System.out.println("[Simple:notifyEnvelope] Secret cards: " + cardString);
			return output;
		}
		
		
	}

	/**
	 * This inner-class handles all local events when messages are received
	 * from the TextEventHandler.
	 * 
	 */ 
	class LocalEvents {
		
		private String showInstructionsFile() {
			String output = "";
			
			/** 
			  * Execute the method simply with the filename as the argument
			  * ie openDocument("index.html");
			  *
			  */
			try {
				Cluedo.API.Resources.openDocument("htmlHelp/index.html");
				output = "Opening HTML help file in your preferred web browser.";
			}
			catch(FileNotFoundException error1) {
				output = "Can't find file the instructions file, possibly you're running a JAR file.";
			}
			catch(RuntimeException error2) {
				output = "Unable to find a suitable program to open the instructions file.";
			}

			
			return output;
		}
		private String localDisproveFeedback(String args) {
			String output = "";
			
			for(int i = 0; i < cardsFound.size(); i++) {
				if(((Card)cardsFound.elementAt(i)).getName().equals(args)) {
					output += "Shown card: " + ((Card)cardsFound.firstElement()).getName();
					gameConfig.setDisproveMode(false);
					output += "\n\n"+gameController.board.getCurrentPlayer().getRealName()+", please finish your turn then type \"end go\".";
					{
						Card testCard = playerWithCard.getCardByName(args);
						output = "\n\n\n\nSuggestion disproved!!\n"+
						playerWithCard.getRealName() + " playing as (" + playerWithCard.getCharacter() +") shows you the \""+testCard.getName()+"\" card which disproves your suggestion.";
						Vector param = new Vector();
						param.add(testCard.getName());
						gameController.directCommand("elminateCard", param);
						param = new Vector();
						param.add(testCard);
						gameController.directCommand("markSuggestMade", param);	
					}
				}
			}

			if(gameConfig.inDisproveMode() == true) {
				output += "Please enter one of the following cards: ";
				for(int i = 0; i < cardsFound.size(); i++)
					output += ((Card)cardsFound.elementAt(i)).getName() + " ";
			}
				
			return output;	
		}
		
		private String localCanGo() {
			String output = "";
			
			if (gameController.board == null)
				output = "No game has been started.\n";
			else if(gameFinished == true) {
				output = "The game has ended, to start a new game type \"startgame\"\n";
			}
			else if(gameController.board.getCurrentPlayer().isInteractive() == false)
				output = "You have been eliminated from the investigation, type \"end go\".\n";

			return output;
		}
		
		private String localErrorInParameters(String inCommand) {
			String output = "";
			
			output = "Invalid usage of: " + inCommand + ". See \"help "+inCommand+"\" for more details.\n";
			
			return output;
		}

		private String localPositions() {
			String output = "";
			String charsPositions = "";

			output = localCanGo();
			if(!output.equals(""))
				return output;


			Vector players = gameConfig.getPlayers();

			output += "Character positions are as follows : -\n ";
			Player pointer = null;
			String preposition = "";
			for(int i = 0; i < players.size(); i++) {
				pointer = ((Player)players.elementAt(i));
				if(pointer.getPosition() instanceof Room)
					preposition  = "in the";
				else
					preposition  = "at";

				if(i == players.size() -1)
					charsPositions += pointer.getRealName() + " is "  + preposition + " " + pointer.getPosition().getID() + ".";
				else
					charsPositions += pointer.getRealName() + " is "  + preposition + " " + pointer.getPosition().getID() + ", ";
			}

			charsPositions = Cluedo.API.StringFuncs.wrapString(charsPositions, 80);

			output += charsPositions + "\n";

			if(gameFinished == true) {
				output += "\nThe game has ended, to start a new game type \"startgame\"\n";
			}
			
			return output;	
		}
		
		
		/** 
		 * This loads a previous game from an XML-file, current needs further testing.
		 *
		 */
		private String localLoadGame(String fileName) {
			String output = "";

			fileName = "../GUI/Saves/" + fileName;
			
			GamePersistance gp = new GamePersistance();
			URL fileUrl = gp.getClass().getResource(fileName);
			
			if(fileUrl == null) {
				System.out.println("Unable to find file.");
				System.exit(-1);
			}		
			File file = new File(fileUrl.getFile());

			fileName = file.getPath();
			System.out.println(fileName);

			boolean fatalException = false;
			
			try { 
				gp.loadGame(fileName);
			}
			catch(IOException error) {
				System.out.println("Unable to load file.");
				fatalException = true;
			}
			catch(NullPointerException error){
				System.out.println("Null pointer...");
				fatalException = true;
			}
			catch(SAXException error){
				System.out.println("Sax exception.");
				error.printStackTrace();
				fatalException = true;
			}
			catch(ParserConfigurationException error){
				System.out.println("Bad parser config exception.");
				fatalException = true;
			}
			catch (Exception error) {
				System.out.println("Other error.");
			}
			gameConfig = gp.getGameConfig();

			if(gameConfig == null)
				fatalException = true;
			
			gameConfig.loadCards();
			
			if(fatalException == false) {
				Vector players = gameConfig.getPlayers();
				if(players == null)
					System.out.println("Players retrieved: Error");
				else
						System.out.println("Players retrieved: "+players.size());
				
			// 	false -- demonstrates an existing game is being loaded
				output += createNewGame(false);
				gameFinished = false;

			}
			else
				output = "Unable to restore game.";
		
			
			return output;
		}
		
		/** 
		 * This dumps the current game to an XML-file, inherently this can't be saved into a .JAR file.
		 *
		 */
		private String localSaveGame(String fileName) {
			String output = "";
			try {
				Resources.dumpTextFile("./Cluedo/GUI/Saves/" + fileName, gameController.board.toXML());
			}
			catch(IOException err) {
				System.out.println("[Simple:localSaveGame] can't write save game.");
			}
			
			return output;
		}
		
		
		private String localMovePlayer(String destination) {
			String output = "";
			
			output = localCanGo();
			
			if(!output.equals(""))
				return output;
			
			
			Player currentPlayer = gameController.board.getCurrentPlayer();
			Position sourceNode = currentPlayer.getPosition();
			Position destinationNode = gameController.board.map.findByID(destination);

			// Player may move
			if(currentPlayer.canMove() == false) {
				return "You may not move again.\n";
			}
			// Player has a position
			else if(sourceNode == null || currentPlayer.getPosition().getID().equals(""))
				return "You have no position.\n";
			// Destination exists			
			else if(destinationNode == null) {
				return "The entered position does not exist, check the spelling.\n";
			}
			// Destination different to source
			else if(destinationNode.getID().equals(sourceNode.getID()) && currentPlayer.canMove() == true) {
				return "You must move to a different position.\n";
			}
			else if( (destinationNode instanceof Square || destinationNode instanceof StartingSquare) &&
					((Square)destinationNode).isOccupied() == true)
			{
					return "The target square is already occupied.";
			}
			else {
						boolean moveAllowed = false;
						
						//TODO : use new path function.
						// Get a path to the target
						int amount = 0;//gameController.board.map.shortestPathTo(sourceNode, 
											//destinationNode);
						
						if(amount == -1) {
							// Special case for moving through secret passage
							if(destinationNode instanceof Room && sourceNode instanceof Room
								&& destinationNode.getNeighbours().contains(sourceNode) == true) {
									moveAllowed = true;
							}
							else {
								if(currentPlayer.hasRolled() == true)
									return "You can't reach that target with "+currentPlayer.getMovesRemaining()+" moves.\n";
								else
										return "You must roll the dice to move.\n";
							}
						}
						else {
							moveAllowed = true;
						}

						
							if(moveAllowed == true) {
								currentPlayer.setMovesRemaining(0);
						
								Vector param = new Vector();
								
								param.add(destinationNode);
								param.add(gameController.board.getCurrentPlayer());
								gameController.directCommand("movePlayer", param);
							}
			}
			
			return output;
		}
				
		private String localQuit() {
			String output = "";

			outputMessage("Byeee.\n");
			System.exit(-1);

			return output;
		}

		private String localGameFormed() {
			String output = "";

			gameConfig.setFormingGame(false);

			return output;
		}	
		
		private String localAddNextPlayer() {
			String output = "";
			String validChars = validNames();  

			output += validChars + 
						"\n\n" +
						defaultCharacterInputMessage;
			
			return output;
		}	
		
		private String validNames() {
			Vector validNames = gameConfig.getValidCharacterNames();
			
			String validChars = "";
			for(int i = 0; i < validNames.size(); i++)
			if(i == validNames.size()-1)
				validChars += i + ". "+ ((String)validNames.elementAt(i)) + ".";
			else
				validChars += i + ". "+ ((String)validNames.elementAt(i)) + ", ";

			validChars = Cluedo.API.StringFuncs.wrapString(validChars, 80);

			validChars = "Select a character from this list: - \n" + validChars;

			return validChars;
		}

		private String localFormingNewGame() {
			String output = "";
			
			gameFinished = false;
			gameConfig = new CluedoConfig();
			gameConfig.loadCards();
			gameConfig.setFormingGame(true);
			
			output = "\nStarting a new game...\n" +
				 lineSeparator + "\n\n" +
			 	 "How many players for the new game (3-6) ?";
			
			return output;
		}

		private String localBadGameSize() {
			String output = "";
			
			output = "The amount of players in the game must be within the range of (3-6).\n\nPlease enter the amount of players for this game (3-6): ";

			return output;
		}
		
		private String localBadNewPlayerFormat() {
			String output = "";

			output += "Player names may only contain letters and numbers and may be a maximum of 20 characters"+
			"\nEnter \"player character\": ";

			return output;
		}		
		
		private String localBadNewPlayer() {
			String output = "";
			String validChars = validNames();  

		
			output = "\nBad format, example: \"alex ReverendGreen\"\n\n";
			
			Vector validNames = gameConfig.getValidCharacterNames();
			
			//validChars = Cluedo.API.StringFuncs.wrapString(validChars, 80);
			output += validChars + 
						"\n\n" +
						defaultCharacterInputMessage;

			return output;
		}		
		private String localAddedNewPlayer() {
			String output = "";
			String validChars = validNames();  

			
			Vector players = gameConfig.getPlayers();
			
			output = (((Player)(players.elementAt(gameConfig.getAmountPlayers()-1))).getRealName()) + " playing as "  +
					(((Player)(players.elementAt(gameConfig.getAmountPlayers()-1))).getCharacter()) + "\n\n";
			
			Vector validNames = gameConfig.getValidCharacterNames();
			

			//validChars = Cluedo.API.StringFuncs.wrapString(validChars, 80);

			output += validChars + 
						"\n\n" +
						defaultCharacterInputMessage;

			return output;
		}
		
		private String localStartNewGame() {
			String output = "";
			
			// true -- represents new game
			createNewGame(true);
			
			return output;
		}

		private String createNewGame(boolean newGame) {
			String output = "";			
			
			if(newGame == true)
				output += "Starting a new game with: " + gameConfig.getPlayers().size() + " players.\n\n";
			else
				output += "Restoring previous game with: " + gameConfig.getPlayers().size() + " players.\n\n";
			
			Vector params = null;
			
			// Make new board
			params = new Vector();
			gameController.directCommand("createBoard", params);
			params = null;
			
			// Add players
			gameController.directCommand("insertPlayers", gameConfig.getPlayers());

			
			// Add squares
			gameConfig.loadBindings();
			
			/* NOTICE:
			 * 
			 * Add cards (no longer loaded here)
			 * gameConfig.loadCards();
			 *
			 * Cards loaded in advance so that character names may be validated
			 * 
			 **/

			
			// Configure map bindings for model
			gameController.directCommand("insertSquares", gameConfig.getMapNodes());
			gameController.directCommand("createBindings", gameConfig.getBindings());
			
			
			// Add boards to game
			params = new Vector();
			params.add(gameConfig.getCards());
			gameController.directCommand("insertCards", params);
			if(newGame == true)
				gameController.directCommand("loadNewDetectivePads", null);
			
			// Add cards to envelope
			gameConfig.loadCards();
			if(newGame == true) {
				params = new Vector();
				params.add(gameConfig.getCards());
			
				gameController.directCommand("setEnvelopeCards", params);
				gameController.directCommand("dealCardsToPlayers" , null);
			}
			
			
			// Put players into their starting positions
			if(newGame == true) {
				params = new Vector();
				params.add("ordered");
				gameController.directCommand("positionPlayers", params);
			
			// 	Chose a player to start the game
				gameController.directCommand("chooseFirstPlayer", null);
			// 	Add turn change feedback here.
			}
			else {
				gameController.directCommand("restorePositions", null);
			}
			
			//myName
			
			// configure any AI players and start threads for them
			Player pointer = null;

			Thread aiThread = null;
			for(int i = 0; i < gameController.board.getPlayers().size(); i++) {
				pointer = ((Player)gameController.board.getPlayers().elementAt(i));
				if(pointer.getPlayerType() >= 1) {
					System.out.println("Found an AI player: " + pointer.getRealName() + " playing as "+ pointer.getCharacter()+".");
					if(AI == true) {
						SimpleAI simpleAI = new SimpleAI("", gameController, pointer.getRealName());
						simpleAI .setMyName(pointer.getRealName());
						AIs.add(simpleAI );
						aiThread = new Thread(simpleAI );
					}
					else {
						ComplexAI complexAI = new ComplexAI("", gameController, pointer.getRealName());
						complexAI.setMyName(pointer.getRealName());
						AIs.add(complexAI);
						aiThread = new Thread(complexAI );
					}

					aiThread.start();
				}
			}

			try {
				// Serves no purpose other than to get AI players "ready"
				gameController.directCommand("AIPlayersOn", null);
			}
			catch(Exception error) {
				System.out.println("[Simple:localStartNewGame] can't start AI players");
			}

			/* Load map areas into the GUI's board component so that we can figure out
			 * what was clicked on.
			 **/
			gameConfig.loadMapArea();
			//board.setMapCodes(gameConfig.getMapAreas());

			
			// Sends a nextPlayer message to all clients representing the start of a new game.
			gameController.directCommand("commenceGame", null);


			/** Let the board listen to game events */
			board.setUplink( gameConfig, gameController );
			
			return output;
		}
		
		private String localEndGo() {
			String output = "";
		
			if (gameController.board == null)
				output = "No game has been started.\n";
			else if(gameFinished == true) {
				output = "The game has ended, to start a new game type \"startgame\"\n";
			}
			
			if(!output.equals(""))
				return output;
			
			gameController.directCommand("nextPlayer", null);
			
			return output;
		}
		
		private String localRunScript(String script) {
			String output = "";
			String line = "";
			String [] parts;
			
	  		try {
	  			StringTokenizer lines = new StringTokenizer(
	  								Cluedo.API.Resources.catTextFile("Scripts/"+script, this), "\n");
	  				while(lines.hasMoreTokens() == true) {
						
						line = lines.nextToken();

						try {
							parts = textParser.parseCommandLine(line);
							if(gameConfig.isFormingGame() == false ) {
								if(parts != null)
									textHandler.handleCommandLine(parts);
								else
									outputMessage("Invalid command, see \"help\"\n");
							}
							else {
								String temp[] = new String[1];
								temp[0] = line;
								textHandler.handleCommandLine( (new String[]{line}) );
							}
						}
						catch(Exception error){
							outputMessage("An error occoured with your previous command.\n");
						}

	  				}
	  		}
	  		catch (Exception general) {
	  			System.out.println("[Simple:localRunScript] Bad script "+script+".\n");

	  		}			
			
			return output;
		}
		private String localShowConsoleHelp(String topic) {
			String output = "";
			String line = "";
			if(topic.equals(""))
				topic = "commands";
				
			try {
				output += "Help topic: '" + topic.replaceAll("_", " ") + "'\n" +
					  lineSeparator + "\n" +
					  Cluedo.API.Resources.catTextFile("Doc/" + topic + ".txt", this);

			}
			catch(Exception notFound) {
				output = "No help topic exists for '" + topic.replaceAll("_", " ") + "'\n";
			}

			return output;
		}

		private String localRollDice() {
			String output = "";
			output = localCanGo();
			if(!output.equals(""))
				return output;
			
			
			Player currentPlayer = gameController.board.getCurrentPlayer();
			
			if(currentPlayer.isInteractive()) {			
				if(currentPlayer.canRoll()) {
					gameController.directCommand("roll", null);
				}
				else
					output = "You have already rolled!!\n";
			}
			return output;
		}
						
		private String localInfo() {
			String output = "";

			output = localCanGo();
			if(!output.equals(""))
				return output;


			output = findOptions();
			return output;
		}
		
		private String findOptions() {
			String output = "";
			Player currentPlayer = gameController.board.getCurrentPlayer();
			
			if(currentPlayer.isInteractive() == false) {
				output = "You have been eliminated from the investigation, type \"end go\".\n";
				return output;
			}
			
			output += "Summary of your turn: -\n";
			

			if(currentPlayer.canMove() == true) {
				if(currentPlayer.hasRolled() == true)
					output += "You are " + currentPlayer.getRealName() + " playing as " + currentPlayer.getCharacter()  + " and may move " + currentPlayer.getMovesRemaining() + " squares.\n";
				else
					output += "You are " + currentPlayer.getRealName() + " playing as " + currentPlayer.getCharacter()  + " and must roll the dice to move.\n";

			}
			else
				output += "You are " + currentPlayer.getRealName() + " playing as " + currentPlayer.getCharacter()  + " and have already moved.\n";  
			if(currentPlayer.getPosition() == null)
				output += "You have no room or square.\n";
			else {
				String grammaticallyCorrect = "at position";
				if(currentPlayer.getPosition() instanceof Room)
					grammaticallyCorrect = "in the";

				output += "You are " + grammaticallyCorrect  + " "+currentPlayer.getPosition().getID()+".\n";
			}

			if(currentPlayer.canMove())
				output += "You may make a move.\n";
			if(currentPlayer.canSuggestHere() && currentPlayer.canSuggest())
				output += "You may make a suggestion here.\n";
			
			
			return output;
		}
		
		/*
		
		9/12/04 - Alex, assisted by Shab
		          This function isn't tested
		
		*/
		private String localShowDetectivePad() {
			String output = "";

			output = localCanGo();
			if(!output.equals(""))
				return output;

			
			Player currentPlayer = gameController.board.getCurrentPlayer();
			
			CardCollection detectiveCards = currentPlayer.getDetectivePad().getCardList();
			
			Card currentCard;
			String suspects = "";
			String rooms = "";
			String weapons = "";
			for(int i = 0; i < detectiveCards.countCards(); i++) {
				currentCard = detectiveCards.cardAt(i);
				if(currentCard.getType().equals("Suspect"))
					if(currentCard.isEliminated() == true) {
						suspects += StringFuncs.
								space(" " + i+ ". " + currentCard.getName(), " ", "[X]", 40) + "\n";
					}
					else {
						suspects += StringFuncs.
							space(" " + i+ ". " + currentCard.getName(), " ", "[ ]", 40) + "\n";
					}
				if(currentCard.getType().equals("Room"))
					if(currentCard.isEliminated() == true) {
						rooms += StringFuncs.
							space(" " + i+ ". " + currentCard.getName(), " ", "[X]", 40) + "\n";
					}
					else {
						rooms += StringFuncs.
							space(" " + i+ ". " + currentCard.getName(), " ", "[ ]", 40) + "\n";
					}
				if(currentCard.getType().equals("Weapon"))
					if(currentCard.isEliminated() == true) {
						weapons += StringFuncs.
							space(" " + i+ ". " + currentCard.getName(), " ", "[X]", 40) + "\n";
					}
					else {
						weapons += StringFuncs.
							space(" " + i+ ". " + currentCard.getName(), " ", "[ ]", 40) + "\n";
					}
			}
			
			
			output = currentPlayer.getRealName() + "'s Detective Pad.\n" +
				 lineSeparator + "\n" +
				 "Rooms:- \n" + rooms + 
				 "Weapons:- \n" + weapons + 
				 "Suspects:- \n" + suspects;

			return output;
		}


		
		/**
		*
		*
		* @updatedby Shab - 07/12/2004
		*/		
		private String localShowCards() {
	            String output = null;
        		String weapon = "";
                        String character = "";
                        String room = "";

       			output = localCanGo();
       			if(!output.equals(""))
       				return output;


			//Define currentplayer as Player and get the current players
			//position.
			Player currentPlayer = gameController.board.getCurrentPlayer();
			//make an instance of CardCollection and get his cards
			CardCollection collection = currentPlayer.getCards();

			  
			//get the number of cards the current player has
			int cardNumber = collection.countCards();
			//do a loop untill of cardnumber size
				for (int i = 0; i < cardNumber; i++) {
				//	get the card type wheather a weapon,character,room
				//	and put it in cardType variable (string).
					String typeCard = collection.cardAt(i).getType();
				//	if the cardtype is a weapon, then
				//	add it to the weapons String list
					if (typeCard.equals("Weapon"))
						weapon += " - " + collection.cardAt(i).getName()+"\n";
				//	else if card type is a character, then
				//	add it to the chracter String list
					else if (typeCard.equals("Suspect"))
						character += " - " + collection.cardAt(i).getName()+"\n";
				//	else if card type is a Room, then
				//	add it to the room String list
					else if (typeCard.equals("Room"))
						room += " - " + collection.cardAt(i).getName()+"\n";
				}
  
			//add weapon, character and room strings to output
			
			output += currentPlayer.getRealName() + "'s Cards.\n" +
				 lineSeparator + "\n";
			if(cardNumber == 0)
				output = "Sorry, you have no cards.\n";
			else {
				output += "You are holding " + cardNumber + " cards.\n\n";

				if(!weapon.equals(""))
					output +=
        	        	 	"Weapons:- \n" + weapon;
				if(!character.equals(""))
              			output += 
	              			"Suspects:- \n" + character;
				if(!room.equals(""))
					output +=
						"Rooms:- \n" + room;
			}

              return output;
        }

		/**
		* Accuse handling
		*
		* @updatedby Alex - 13/01/2005
		* @updatedby Liam - 07/12/2004
		*/		

		private String localAccuse(String who, String what, String where) {
			String output = "";

			// If game hasn't started yet return error.
			output = localCanGo();
			if (!output.equals(""))
				return output;

			Player currentPlayer = gameController.board.getCurrentPlayer();
			
			// If player is trying to make an accusation and is out of
			// investigation, return error.
			if (gameController.board.getCurrentPlayer().isInteractive() == false)
				return "You are unable to make an accusation because you are no longer in the investigation.\n";

			//	Declare local accused and envelope cards for comparison
			Card charEnvelope = null;
			Card weaponEnvelope = null;
			Card roomEnvelope = null;
			
			Card suspectCard = null;
			Card roomCard = null;
			Card weaponCard = null;

			CardCollection envelope = ((CardCollection) gameController.board.getEnvelope());

			//	Find card order in envelope and assign to local cards.
			roomEnvelope = envelope.cardAt(0);
			charEnvelope = envelope.cardAt(1);
			weaponEnvelope = envelope.cardAt(2);
			

			int cardIndex = -1;

			// Loop-up the numbers if required. 
			try {
				cardIndex = Integer.parseInt(who);
				who = gameController.board.getCardPack().cardAt(cardIndex).getName();
			}
			catch (Exception error) {}

			try {
				cardIndex = Integer.parseInt(what);
				what = gameController.board.getCardPack().cardAt(cardIndex).getName();
			}
			catch (Exception error) {}
			try {
				cardIndex = Integer.parseInt(where);
				where = gameController.board.getCardPack().cardAt(cardIndex).getName();
			}
			catch (Exception error) {}			
			
			// Resolve card objects..
			suspectCard = currentPlayer.getDetectivePad().hasCard(who);
			roomCard = currentPlayer.getDetectivePad().hasCard(where);
			weaponCard = currentPlayer.getDetectivePad().hasCard(what);

			if( suspectCard.getType().equals("Suspect") && 
					roomCard.getType().equals("Room") &&
					weaponCard.getType().equals("Weapon") )
			{
				//	Compare envelope cards with accused cards.

				//	Continue only if the accusation exactly correct.
				if (charEnvelope.getName().equals(suspectCard.getName()) &&
						weaponEnvelope.getName().equals(weaponCard.getName()) &&
						roomEnvelope.getName().equals(roomCard.getName()))
				{
					//	If exact match found then end game.
					gameController.directCommand("winGame", null);

				} else {
					// If exact match NOT found then remove player from active pool.
					
					gameController.directCommand("removeInteractive", null);

					//	Check to see that there are still more people playing, if not end game and give answer to puzzle.

					int count = gameController.board.countInteractivePlayers();
					
					// accuse MrsWhite Rope Hall
					if(count > 0)
						output += gameController.board.getCurrentPlayer().getRealName() +
							"'s accusation was incorrect, but remains in the investigation to disprove suggestions!\n";
					else if (count == 0) {
						output += gameController.board.getCurrentPlayer().getRealName() + "'s accusation was incorrect!\n"
						        + "\n\nThe game is over, the mystery could not be solved!\n"
								+ "\nThe murder was commited by "
								+ charEnvelope.getName()
								+ " with the "
								+ weaponEnvelope.getName()
								+ " in the " + roomEnvelope.getName() + ".\n\n"
								+ "To play again, type 'startgame' or 'quit' to leave the game.";
						gameFinished = true;
					}
					else {
						gameController.directCommand("nextPlayer", null);
					}
				}
			}
			else {
					output = "Please enter an accusation in this format:\n accuse MrsWhite Dagger DiningRoom\n";

			}

			return output;
		}
		
	

	/**
	*
	*
	* @updatedby Liam & Alex - 07/12/2004
	*/		
	private String localEliminateCard(String targetCard) {
		String output = "";
		output = localCanGo();
		if(!output.equals(""))
			return output;

		
		// Find detective pad from board
		Cluedo.Game.DetectivePad detectivePad = null;
		detectivePad = gameController.board.getCurrentPlayer().getDetectivePad();
		Player currentPlayer = gameController.board.getCurrentPlayer();
		Cluedo.Game.Card targetCardObject = null; 

		
		// Error in detective pad
		if(detectivePad == null) {
				return "Detective pad is empty.\n";
		}
		
		try {
			int cardIndex = Integer.parseInt(targetCard);
			targetCard = gameController.board.getCardPack().cardAt(cardIndex).getName();
		}
		catch(Exception error) {
		}
		
		targetCardObject = detectivePad.hasCard(targetCard);		


		if(targetCardObject == null) {
			output = "Unable to find such a card.\n";
		}
		else {
			Vector param = new Vector();
			param.add(targetCard);
			gameController.directCommand("elminateCard", param);
			output = targetCard +" has been marked as eliminated.\n";
		}

		return output;
	}
	
	/**
	*
	*
	* @updatedby Liam & Alex - 07/12/2004
	*/		
	private String localUnEliminateCard(String targetCard) {
		String output = "";
		output = localCanGo();
		if(!output.equals(""))
			return output;

		
		// Find detective pad from board
		Cluedo.Game.DetectivePad detectivePad = null;
		detectivePad = gameController.board.getCurrentPlayer().getDetectivePad();
		Player currentPlayer = gameController.board.getCurrentPlayer();
		Cluedo.Game.Card targetCardObject = null; 

		
		// Error in detective pad
		if(detectivePad == null) {
				return "Detective pad is empty.\n";
		}
		
		try {
			int cardIndex = Integer.parseInt(targetCard);
			targetCard = gameController.board.getCardPack().cardAt(cardIndex).getName();
		}
		catch(Exception error) {
		}
		
		// See if we can find the card
		targetCardObject = detectivePad.hasCard(targetCard);

		if(targetCardObject == null) {
			output = "Unable to find such a card.\n";
		}
		else {
			Vector param = new Vector();
			param.add(targetCard);
			gameController.directCommand("unElminateCard", param);
			output = targetCard +" has been un marked as eliminated.\n";
		}

		return output;
	}

	private String localSuggest(String who, String what) {
		String output = "";
		
		Card testCard = null;
		Player foundOn = null;
		boolean found = false;
		boolean validCards = true;
		
		output = localCanGo();
		if(!output.equals(""))
			return output;

		Player currentPlayer = gameController.board.getCurrentPlayer();
		Vector players = gameController.board.getPlayers();
		Cluedo.Game.DetectivePad detectivePad = null;
		detectivePad = gameController.board.getCurrentPlayer().getDetectivePad();

		if(currentPlayer.canSuggestHere() == false) {
			output = "You can't make a suggestion here.";
		}
		else if(currentPlayer.isSuggestionOnLastTurn() == true)
			output = "You can't make multiple suggestions without leaving the room.";
		else if(currentPlayer.canSuggest() == true) {
			CardCollection gameCards = gameController.board.getCardPack();
			
			Card suspect = null;
			Card weapon = null;
			Card room = gameCards.cardByName(currentPlayer.getPosition().getID());
			
			int cardIndex = -1;
			
			try {
				cardIndex = Integer.parseInt(who);
				who = gameController.board.getCardPack().cardAt(cardIndex).getName();
			}
			catch(Exception error) {}
			try {
				cardIndex = Integer.parseInt(what);
				what = gameController.board.getCardPack().cardAt(cardIndex).getName();
			}
			catch(Exception error) {}
			
			suspect = detectivePad.hasCard(who);
			weapon = detectivePad.hasCard(what);

			if(suspect == null || weapon == null || room == null) {
				validCards = false;
				output = "You entered an invalid card.\n";
			}
			else {
				if((weapon.getType().equals("Weapon")) && (suspect.getType().equals("Suspect"))) {
					
					// Call suspect into room.
					
					Player defendant = gameController.board.getCharacter(suspect.getName());
					
					if(!currentPlayer.getCharacter().equals(suspect.getName())) {
						if(defendant != null) {
							Position oldRoom = defendant.getPosition();
							Position destination = currentPlayer.getPosition();
							
							Vector parameters = new Vector();
							parameters.add(destination);
							parameters.add(defendant);		// Who to move..
							gameController.directCommand( "movePlayer", parameters );
						}
					}
                                	
					// Search all players for a card.

					
					playerWithCard = null;
					cardsFound = new Vector();
					
					// Order the players so we can scan through and check who has cards in the right order.
					Vector playersToParse = localArrangePlayersBy(currentPlayer);

					for(int i = 0; i < playersToParse.size(); i++) {
						playerWithCard = (Player)playersToParse.elementAt(i);
						if(playerWithCard != currentPlayer) {
							if(playerWithCard.hasCard(suspect))
								cardsFound.add(suspect);
							if(playerWithCard.hasCard(weapon))
								cardsFound.add(weapon);
							if(playerWithCard.hasCard(room))
								cardsFound.add(room);
							if(cardsFound.size() > 0)
								break;
						}
					}
					
					if(cardsFound.size() > 0) {
						output += playerWithCard.getRealName() + " choose which card you will use to disprove :-\n ";
						for(int i = 0; i < cardsFound.size(); i++) {
							output += ((Card)cardsFound.elementAt(i)).getName() + " ";
						}
						gameConfig.setDisproveMode(true);
					}
					else {
						output += "Unable to disprove the suggestion.";
					}
					gameController.directCommand("setSuggestionOnLastTurn", null);
				
				}
			}
			
			
		}
	
		return output;
	}

	/** Order the players so we can scan through and check who has cards in the right order.
	 * 
	 */
	private Vector localArrangePlayersBy(Player currentPlayer) {
		Vector players = new Vector();
		int playerPosition = gameController.board.getPlayerIndex(currentPlayer);
		Vector livePlayers = gameController.board.getPlayers();
		
		// Add all elements after the player
		for(int i = playerPosition + 1; i < livePlayers.size(); i++)
			players.add(livePlayers.elementAt(i));
		// Add all elements before the player
		if(!( playerPosition == 0)) 
				for(int i = 0; i < playerPosition; i++)
					players.add(livePlayers.elementAt(i));
		return players;

	}
  }
  
	/* Application code and handling here */
}
